/*
 * Copyright (C) 2011 Rune Sætre
 *	for the UbiCompForAll project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * TODO: Partial term match, context menu for disambiguation.
 * TODO: Link to UbiCompForAll homepage
 * TODO: More terms from Work Package WP2, and the Meta-model
 */

package org.ubicompforall.ubiterms;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.ExpandableListActivity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.AbsListView;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;
import android.widget.Toast;

/********************************************************************
 * @author Rune Sætre
 * @since 2011.05.12
 *
 * UbiTerms is an Activity, with a Dictionary and some Buttons
 */
public class UbiTerms extends ExpandableListActivity{

	//CONSTANTS
	private static final int DEBUG = 1;
	private static final String TAG = "UbiTerms";

	//private static final String visKey = "visKey";
	private static final String termfileFirstName = "terms";
	private static final String figureFilename = "BusinessAspectModel_Stakeholders.png";
	//MEMBERS
	WebView myWebView; //Links all the term definitions together with A HREF's
	DictionaryListAdapter termListAdapter; // Reads TermCollection, provides TermView

	//PUBLIC METHODS

	private void debug(int level, String msg) {
		if ( DEBUG >= level ){
			Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
		}
	}//debug

	@SuppressWarnings("unused")
	private void debug(int level, String msg, int lengthLong) {
		if ( DEBUG >= level ){
			Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
		}
	}//debug(long), e.g. slow

	public void goForward(View v){
		debug(2, "Call WebView.goForward, if appropriate here");
		//if ( ! myWebView.canGoBackOrForward(2).canGoForward() ){
		if ( ! myWebView.canGoBackOrForward(2) ){
			findViewById(R.id.forward_button).setVisibility(View.GONE);
		}//can NOT goForward any more
		myWebView.goForward();
	}//goForward

	private DictionaryListAdapter initDictionaryList(String termfilefirstname, Bundle savedInstanceState) {
		// Use our own ExpandableListAdapter
		termListAdapter = new DictionaryListAdapter(this, termfileFirstName);
		if (savedInstanceState == null){
			termListAdapter.initTermlistAdapter();
		}else{
			termListAdapter.onRestoreSavedInstance(savedInstanceState);
		}//If first instance, else re-use
		return termListAdapter;
	}//initDictionaryList

	public WebView initWebView(Bundle savedInstanceState){
		writeLog("initWebView");
		myWebView = (WebView)findViewById(R.id.entryDef);
		myWebView.loadData(""
				+"<INPUT type=button onClick=\"showAndroidToast('Hello Android!')\" />"
				+"<script type=\"text/javascript\">"
				+"  function showAndroidToast(toast) {"
				+"		Android.showToast(toast);"
				+"	}"
				+"</script>"

				+"<IMG SOURCE=\""+figureFilename+"\">"

				+"Click a term in the list...", "text/hml", "utf-8");
		myWebView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
		myWebView.setWebViewClient( new MyWebViewClient() );
		myWebView.getSettings().setJavaScriptEnabled(true);
		myWebView.getSettings().setBuiltInZoomControls(true);
		
		if (savedInstanceState != null){
			myWebView.restoreState(savedInstanceState);
		}
		writeLog("initWebView...Done!");
		return myWebView;
	}//initWebView

	@Override
	public boolean onChildClick( ExpandableListView parent, View v,
			int groupPosition, int childPosition, long id ){
		myWebView.loadData(termListAdapter.getDef(groupPosition, childPosition), "text/hml", "utf-8");
		return true; //click was handled (true)
	}//onChildClickListener)

	@Override
	public void onCreate(Bundle savedInstanceState) {
		try{
			super.onCreate(savedInstanceState);
			writeLog("OnCreate() called");
			setContentView(R.layout.dictionary_layout);
			myWebView = initWebView( savedInstanceState );

			termListAdapter = initDictionaryList(termfileFirstName, savedInstanceState);
			setListAdapter( termListAdapter );

			//debug(1, "my getExpandableListView() is "+getExpandableListView(), Toast.LENGTH_LONG );
			registerForContextMenu( getExpandableListView() );//Gets andoid:id/list
			//viewFig1( myWebView );
		}catch (Exception e){
			writeLog("Caught an exception in onCreate: e is "+e);
			for(StackTraceElement ste: e.getStackTrace()){
				writeLog("\ntrace: "+ste.toString() );
			}
			writeLog("Bundle is "+savedInstanceState+", with size="+savedInstanceState.size());
		}//try-catch
	}//onCreate

	@Override
	public void onCreateContextMenu ( ContextMenu menu, View v, ContextMenuInfo menuInfo ) {
		//super.onCreateContextMenu(menu, v, menuInfo);
		debug(1, "Making context menu...");
	    menu.setHeaderTitle("UbiTerm menu");
	    menu.add(0, 0, 0, R.string.ubiterms_context_menu);
	}//onCreateContextMenu

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		// Check if the key event was the BACK key and if there's history
		if ( (keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack() ) {
			//myWebView.setVisibility(View.VISIBLE);
			myWebView.goBack();
			findViewById(R.id.forward_button).setVisibility(View.VISIBLE);
			return true;
		}else if ( keyCode == KeyEvent.KEYCODE_MENU ){
			toggle_menu();
			return true;
		}else if ( keyCode == KeyEvent.KEYCODE_SEARCH ){
			debug(1, "All users are "+myWebView.findAll("user") );
			search();
		}//if menu/back/search buttons are pressed
		// If it wasn't the MENU, or BACK keys or there's no web page history, bubble up to the default
		// system behavior (probably exit the activity)
		return super.onKeyDown(keyCode, event);
	}//onKeyDown

	@Override
	protected void onPause(){
		super.onPause();
		writeLog("OnPause() called");
		debug(2, "On Pause...");
	}//onPause

	@Override
	protected void onRestart(){
		super.onRestart();
		writeLog("OnRestart() called");
		debug(2, "On Restart...");
	}//onStart

	@Override
	protected void onResume(){
		super.onResume();
		writeLog("OnResume() called");
		debug(2, "On Resume...");
	}//onResume

	@Override
	protected void onSaveInstanceState (Bundle outState){
		super.onSaveInstanceState(outState);
		debug(1, "Saving current list for later...");
		debug(2, "On Save... TODO: Store Back-log from WebView!!");
		//outState.putSerializable(, termListAdapter.vis);
		outState.putSerializable(termfileFirstName, termListAdapter.mTC);
	}//onSaveInstanceState

	@Override
	protected void onStart(){
		super.onStart();
		writeLog("OnStart() called");
		debug(2, "On Start...");
	}//onStart

	@Override
	protected void onStop(){
		super.onStop();
		writeLog("OnStop() called");
		debug(2, "On Stop...");
	}//onStop

	public void quit(View v) {
		debug(2, "Quitting...");
		finish();
	}//quit

	public void reset(View v) {
		debug(2, "Resetting...");
		setContentView(R.layout.dictionary_layout);
		initWebView( null );
		//termListAdapter.readFromFile(termFileName);
	}//reset
/*
	public void restart(View v){
		Intent intent = getIntent();
		finish();
		startActivity(intent);
	}//restart
*/
	private void search() {
		debug(1, "Search for a term now!");
	}//search

	public void sort(View v) {
		debug(1, "Sort id is "+getString(R.string.ubiterms_sort_id) );
		termListAdapter.sortAndHighlight();
	}//sort

	private void toggle_menu() {
		//boolean visible = findViewById(R.id.ubiterms_menu).getVisibility();
		View menu = findViewById(R.id.ubiterms_menu);
		if ( menu.getVisibility() == View.GONE ){
			menu.setVisibility( View.VISIBLE );
		}else{
			menu.setVisibility( View.GONE );
		}
	}//toggle_menu

//	@SuppressWarnings("unused")
	public void viewFig(View v) {
		Intent intent = new Intent( Intent.ACTION_VIEW );
		//intent.setData( "Test" );

		startActivity(intent);
	}//viewFig1

    private void writeLog(String msg) {
    	if (DEBUG>0){
            android.util.Log.d(TAG, msg);
    	}
    }//writeLog


/////// END UbiTerms CLASS Methods, PUBLIC and PRIVATE CLASSES below //////////////////////////


	/**
     * A simple adapter which maintains the TermCollection.
     *
	 * This DictionaryListAdapter presents content from the TermDictionary
	 * Organized into "viewpoints", terms and defs. (ExpandableListAdapter)
	 */
	private class DictionaryListAdapter extends BaseExpandableListAdapter {
		public DictionaryListAdapter(Context mainActivity, String termfileName){
			debug(2, "DictionaryListAdapter CONSTRUCTOR");
			mContext = mainActivity;
			mTermfileName = termfileName;
			mTC = new TermCollection();
		}//CONSTRUCTOR

		public void initTermlistAdapter() {
			termListAdapter.readFromFile(mTermfileName);
			termListAdapter.sortAndHighlight();
		}//initTermlistAdapter

		Context mContext; //My main activity
		String mTermfileName; //The file with all the terms
		//Store information from the project dictionary file (somewhere, like here)
		TermCollection mTC;

		// PUBLIC METHODS

		private ArrayList<String> extractLinkTerms( TreeMap<String,String> sorted ) {
			ArrayList<String> linkTerms = new ArrayList<String>();
			for ( String term : sorted.keySet() ){
				linkTerms.addAll( extractWords(term) );
			}//for each term, match upper/lower versions to the def
			Collections.sort(linkTerms, new byLineLength());

			return linkTerms;
		}//extractLinkTerms

		public ArrayList<String> extractWords(String term){
			ArrayList<String> linkTerms = new ArrayList<String>(); //E.g. "(End-user Development)|(EUD)"

			//Extract Full Form and Acronym
			String full="", acro="";
			if ( term.matches(".* \\(.*") ){
				full=term.replaceFirst("(.+) \\(.*", "$1");
				acro=term.replaceFirst(".+ \\((.*)\\)", "$1");
				debug(2, "acro is "+acro+", full is "+full+", links="+linkTerms);
			}else{
				full = term;
			}
			linkTerms.add(full);

			//Add Acronym to the RegExp-part
			if ( !acro.equals("") ){
				linkTerms.add(acro);
			}

			//Add All Single Words from the full form
			linkTerms.addAll(Arrays.asList( full.split("[- ]") ));

			return linkTerms;
		}//extractWords

		@Override
		public Object getChild(int groupPosition, int childPosition) {
			return mTC.getTerm( groupPosition, childPosition);
		}

		@Override
		public long getChildId(int groupPosition, int childPosition) {
			return childPosition;
		}

		/**
		 * Make a EntryView(TextView) to hold each dictionary term.
		 **/
		@Override
		public View getChildView(int groupPos, int childPos,
		 boolean isLastChild, View convertView, ViewGroup parent) {
			TextView entryView;// = (TextView) convertView;
			if (convertView == null) {
				entryView = new TextView(mContext);
				//debug(2, "my class is "+entryView.getClass());
			}else{
				entryView = (TextView) convertView;
			}
			entryView.setTextSize(16);
			entryView.setTextColor( getResources().getColor( R.color.solid_green ) );
			entryView.setText( mTC.getTerm(groupPos, childPos) );
			return entryView;
		}//getChildView

		@Override
		public int getChildrenCount(int groupPosition) {
			return mTC.getCatHash(groupPosition).size();
		}

		public String getDef(String fragment) {
			//fragment = fragment.replaceAll("%20", " ");
			debug(2, "fragment is "+ fragment);
			for ( String term : mTC.get( getString(R.string.ubiterms_sort_id) ).keySet() ){
				if (term.matches("(?i).*"+fragment+".*") ){
					fragment = term;
					debug(1, "Matched "+fragment);
					//break; // Somehow, use all! (-stopwords)
				}//found a matching term definition to show
			}//for possible terms

			String html="<B>"+fragment+"</B> ";
			html += mTC.get( getString(R.string.ubiterms_sort_id) ).get(fragment);
			return html;
		}//getDef( term )

		public String getDef(int groupPosition, int childPosition) {
			String html="<B>"+mTC.getTerm(groupPosition, childPosition)+"</B> ";
			html += mTC.getDef(groupPosition, childPosition);
			return html;
		}//getDef

        public TextView getGenericView() {
            // Layout parameters for the ExpandableListView
            AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT, 64);

            TextView textView = new TextView(UbiTerms.this);
            textView.setLayoutParams(lp);
            // Center the text vertically
            textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
            // Set the text starting position
            textView.setPadding(60, 0, 0, 0);
            return textView;
        }//getGenericView

		@Override
		public Object getGroup(int groupPosition) {
			debug(2, "Returning group("+groupPosition+") is "+mTC.getCat(groupPosition));
			return mTC.getCat(groupPosition);
		}

		@Override
		public int getGroupCount() {
			debug(2, "GroupCount is "+mTC.size());
			return mTC.size();
		}

		@Override
		public long getGroupId(int groupPosition) {
			return groupPosition;
		}

		@Override
		public View getGroupView(int groupPosition, boolean isExpanded,
		 View convertView, ViewGroup parent) {
            TextView textView = getGenericView();
            textView.setText(getGroup(groupPosition).toString());
            textView.setTextColor( getResources().getColor(R.color.solid_red) );

            return textView;
		}//getGroupView

		@Override
		public boolean hasStableIds() {
			return false;
		}

		@Override
		public boolean isChildSelectable(int groupPosition, int childPosition) {
			return true;
		}

		public Matcher makeMatcher( ArrayList<String> linkTerms ){
			//Setup a regex to link to all terms mentioned in the definitions
			//Don't match inside a word, except when the next character is (plural) (e)s
			StringBuilder buf = new StringBuilder( "(?i)(^|[^a-zA-Z])(" );
			for (String term: linkTerms) buf.append(term).append("|");
			buf.deleteCharAt(buf.length()-1); //debug(1, "final character is "+buf.charAt(buf.length()-1) );
			buf.append(")([^a-df-rt-z]|$)");
			Pattern pattern = Pattern.compile( buf.toString(), Pattern.UNICODE_CASE );
			debug(2, "pattern is "+buf );
			//writeLog( "pattern is "+buf );
			return pattern.matcher("");
		}//makeMatcher

		@SuppressWarnings("unchecked")
		public void onRestoreSavedInstance(Bundle savedInstanceState) {
			mTC = new TermCollection();
			try{
				HashMap<String,Object> termCollection
				= (HashMap<String,Object>) savedInstanceState.getSerializable(termfileFirstName);
				for ( Entry<String,Object> cat: termCollection.entrySet()){
					for( Entry<String,String> term: ((HashMap<String,String>) cat.getValue()).entrySet()){
						mTC.add(cat.getKey(), term.getKey(), term.getValue());
					}//for each term
				}//for each category
			}catch (ClassCastException e){
				writeLog("Exception in onRestoreSavedInstance: "+e);
				for(StackTraceElement ste: e.getStackTrace()){
					writeLog("\ntrace: "+ste.toString() );
				}//for each method-call
				mTC=null;
			}//try-catch stored Terms
			if (mTC == null){
				writeLog("Re-read the mTC TermCollection that was lost!");
				initTermlistAdapter(); //Pretend it's the first time...
				debug(1, "Re-init from file <"+termfileFirstName+">");
			}//if re-read
			writeLog("onRestoreSavedInstance...Success!");
		}//onRestoreSavedInstance

		public void readFromFile(String filename){
			mTC.readTextfile(filename+".txt");
			//mTC.readHtmlfile(filename+".html");
			writeLog("Reading from file "+filename+"... Done!");
		}//readFromFile

		public void sortAndHighlight() {
			TreeMap<String,String> sorted = mTC.sort();
			//this.clear(); //Remove the terms from their original places before adding sorted
			notifyDataSetChanged();

			ArrayList<String> linkTerms = extractLinkTerms( sorted );
			Matcher matcher = makeMatcher( linkTerms );

			for ( String cat : mTC.keySet() ){
				for ( Entry<String, String> pair : mTC.get(cat).entrySet() ){
					matcher.reset( pair.getValue() );
					String html = matcher.replaceAll( "$1<A HREF=\"http:////#$2\">$2</A>$3" )
							+ "<BR><font color=red>"+cat+"</font> [<A HREF=http://#hide>hide</A>]";
					//html.replaceAll( "(?i)("+pair.getKey()+")", "<U>$1</U>");
					pair.setValue( html );
					sorted.put( pair.getKey(), pair.getValue() );
				}//for each definition
			}//for each category
			mTC.put( getString(R.string.ubiterms_sort_id), sorted );
		}//sortAndHighlight

		//HELPER CLASS
		public class byLineLength implements java.util.Comparator<String> {
			 public int compare(String a, String b) {
				 return b.length() - a.length();
			 }//compare
		}//class byLineLength

	}//class DictionaryListAdapter


//////////////////////////////////////////////////////////////////////////////////


	public class JavaScriptInterface {
	    Context mContext;

	    /** Instantiate the interface and set the context */
	    JavaScriptInterface(Context c) {
	        mContext = c;
	    }

	    /** Show a toast from the web page */
	    public void showToast(String toast) {
	        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
	    }//showToast
	}//class JavaScriptInterface


/////////////////////////////////////////////////////////////////////////////////


	private class MyWebViewClient extends WebViewClient {

		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			debug(2, "url is "+ url);
			if ( Uri.parse(url).getFragment() != null ) {
				// This is my web site, so do not override; let my WebView load the page
				String fragment = Uri.parse(url).getFragment(); //Everything after "#"
				if ( fragment.equalsIgnoreCase("hide") ){
					view.loadData("<HTML>[<A HREF=http://#show>show</A>]</HTML>", "text/plain", "utf-8");
				}else if ( fragment.equalsIgnoreCase("show") ){
					view.goBack();
					findViewById(R.id.forward_button).setVisibility(View.VISIBLE);
					return true; //The WebView will use it's previous size, matching the previous content
				}else{
					myWebView.loadData( termListAdapter.getDef( fragment ), "text/hml", "utf-8");
				}
				return false; //To make sure the WebView shrinks itself on a refresh
			}else{
				//The link is not for a safe site,
				// so launch another Activity that handles URLs
				Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
				intent.setData(Uri.parse(url));
				startActivity(intent);
			}//If safe local site, else external viewer
			return false; //The WebView must handle other url's
		}//shouldOverrideUrlLoading

	}//class MyWebViewClient


/////////////////////////////////////////////////////////////////////////////////



	/**
	 * TermCollection is a hash from Category -> Term -> Def
	 *
	 * TreeMap< String(Cat), TreeMap<String(Term),String(Definition)> >
	 */
	private class TermCollection extends
	 TreeMap<String, TreeMap<String,String> > implements Serializable{
		public TermCollection(){
			//Import the project terms from a given file (in assets)
			debug(2, "TermCollection CONSTRUCTOR, run init()!");
		}//CONSTRUCTOR

		/** Generated unique identifier for Serializing purposes */
		private static final long serialVersionUID = 6066867750016363520L;

		public void add(String cat, String term, String def) {
			debug(3, "Adding cat="+cat+", term="+term+", def("+def.length()+")");
			if (get(cat) == null){
				debug(2, "Missing cat("+cat+")");
				put(cat, new TreeMap<String,String>() );
			}
			if (get(cat).get(term) != null){
				debug(0, "DOUBLE TERM is "+term);
			}
			get(cat).put(term, def);
			debug(3, "Found terms("+size()+")");
		}//add

		private String getCat(int groupPosition) {
			Iterator<String> cat = this.keySet().iterator();
			for (int i=0; i<groupPosition; i++){
				cat.next();
			}
			return cat.next();
		}//getCategory

		private TreeMap<String,String> getCatHash(int pos){
			Iterator<String> cat = keySet().iterator();
			for (int i=0; i<pos; i++){
				cat.next();
			}
			return get(cat.next());
		}//getCategory

		public String getDef(int groupPos, int childPos) {
			Iterator< String > it = getCatHash(groupPos).values().iterator();
			for (int i=0; i<childPos; i++){	it.next(); }
			String def = it.next();
			return def;
		}//getDef(group,pos)

		public CharSequence getTerm(int groupPos, int childPos) {
			Iterator<String> termIt = getCatHash(groupPos).keySet().iterator();
			for (int i=0; i<childPos; i++){	termIt.next(); }
			return termIt.next();
		}//getTerm

		@SuppressWarnings("unused")
		private int getTermCount() {
			int count=0;
			for( TreeMap< String, String > terms: values() ){
				count += terms.size();
			}//enhanced for-loop, a.k.a. for-each
			return count;
		}//getTermCount

		/* Programmatically load text from an asset and place it into the dictionary
		 *  Note that the text we are loading is ASCII, so we need to convert it to UTF-16.
		protected void readHtmlfile(String filename){
			//ArrayList<String> terms, ArrayList<String> defs, ArrayList<Integer> mExpanded)
			try {
				InputStreamReader isr = new InputStreamReader( getAssets().open(filename), "UTF-8" );
				debug(1, "Encoding is "+isr.getEncoding() );
				BufferedReader br = new BufferedReader( isr );

				//Get the title
				String title;
				if ( br.ready() ){
					title = br.readLine();
					debug(1, "Title is "+title );
				}

				// Convert the buffer into a dictionary, line by line
				int termCount = 0;
				String category="default";
				while ( br.ready() ){
					String line = br.readLine();
					debug(1, "Line is "+line );
					//Skip blank lines...
					while ( line.length() <=1 && br.ready() ){
						line = br.readLine();
					}
					//Set viewpoint
					String[] cols = line.split("\t");
					if (cols.length ==1){
						category = cols[0];
						if ( br.ready() ){
							line = br.readLine();
							cols = line.split("\t");
						}
					}
					//Add dictionary term
					if (cols.length ==2 && cols[0].length() >1 ){
						add(category, cols[0], cols[1]);
						termCount++;
					}else{
						debug(1, "BAD: cols.length is "+cols.length );
					}
				}//while lines
			} catch (IOException e) {
				throw new RuntimeException(e);  // Should never happen!
			}
		}//readHtmlfile
*/

		/* Programmatically load text from an asset and place it into the dictionary
		 *  Note that the text we are loading is ASCII, so we need to convert it to UTF-16.
		 */

		protected void readTextfile(String filename){
			//ArrayList<String> terms, ArrayList<String> defs, ArrayList<Integer> mExpanded)
			try {
				InputStreamReader isr = new InputStreamReader( getAssets().open(filename), "UTF-16" );
				debug(2, "Encoding is "+isr.getEncoding() );
				BufferedReader br = new BufferedReader( isr );

				//Get the title
				String title="BLANK";
				if ( br.ready() ){
					title = br.readLine();
					debug(1, "Title is "+title );
				}

				// Convert the buffer into a dictionary, line by line
				//int termCount = 0;
				String category="default";
				while ( br.ready() ){
					String line = br.readLine();
					//Skip blank lines...
					while ( line.length() <=1 && br.ready() ){
						line = br.readLine();
					}
					//Set viewpoint
					String[] cols = line.split("\t");
					if (cols.length ==1){
						category = cols[0].trim();
						if ( br.ready() ){
							line = br.readLine();
							cols = line.split("\t");
						}
					}
					//Add dictionary term
					if (cols.length ==2 && cols[0].length() >1 ){
						add( category, cols[0].trim(), cols[1].trim() );
						//termCount++;
					}else{
						debug(3, "BAD: cols.length is "+cols.length );
					}
				}//while lines
			} catch (IOException e) {
				throw new RuntimeException(e);  // Should never happen!
			}
		}//readTextfile
/*
		public void setDef(int groupPos, int childPos, String newDef) {
			Hashtable< String, String > catHash = getCatHash(groupPos);
			Iterator< String > it = catHash.values().iterator();
			for (int i=0; i<childPos; i++){
				it.next();
			}//for skipping children
			String oldDef = it.next();
			debug(2, "Searching def <"+childPos+"> in <"+catHash.size()+"> terms");
			debug(2, "found ("+oldDef.getClass()+")");
		}//setDef
*/
		public TreeMap<String,String> sort() {
			TreeMap< String,String > sorted = new TreeMap<String,String>();
			for( Entry< String, TreeMap<String,String> > catTerms : entrySet() ){
				for ( Entry<String,String> term : catTerms.getValue().entrySet() ){
					sorted.put(term.getKey(), term.getValue() );
				}
			}//for each viewpoint/category
			debug(2, "all sorted size is "+sorted.size());
			return sorted;
		}//sort

	}//class TermCollection

}//class UbiTerms


//////////////////////////////////////////////////////////////////////////////////


//OLD STUFF

/**
*  Old 1-D stuff is basically the same as Android.widget.extendableListConnector...
*
* REALLY COOL STUFF
*

private class YourWebClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.contains("mailto")) {
            String email = "";
            sendEmail();
            return super.shouldOverrideUrlLoading(view, url);
        }
        view.loadUrl(url);
        return true;
    }
}
public void sendEmail(String email){
    final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
    emailIntent.setType("plain/text");
    emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{email});

    String mySubject = "this is just if you want";
    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, mySubject);
    String myBodyText = "this is just if you want";
    emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, myBodyText);
    context.startActivity(Intent.createChooser(intent, "Send mail...));
}

*
*/

